package com.ruoyi.merchant.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.merchant.service.ShopRepository;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.ruoyi.merchant.mapper.ShopMapper;
import com.ruoyi.merchant.domain.Shop;
import com.ruoyi.merchant.service.IShopService;

import javax.annotation.Resource;

/**
 * 店铺：存储店铺的基本信息Service业务层处理
 *
 * @author ruoyi
 * @date 2025-09-15
 */
@Service
public class ShopServiceImpl implements IShopService {
    @Resource
    private ShopMapper shopMapper;

    @Resource
    private ShopRepository shopRepository;
    @Resource
    private ElasticsearchRestTemplate esTemplate;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private static final String HISTORY_KEY_PREFIX = "shop:history:";
    private static final int MAX_HISTORY = 5;

    private static final String SHOP_GEO_KEY = "shop:location";

    // 把店铺经纬度存入 Redis
    public void saveShopLocation(List<Shop> shopList) {
        for (int i = 0; i < shopList.size(); i++) {
            Shop shop = shopList.get(i);
            if (shop.getLongitude() != null && shop.getLatitude() != null) {
                Double longitude = Double.parseDouble(shop.getLongitude());
                Double latitude = Double.parseDouble(shop.getLatitude());
                redisTemplate.opsForGeo().add(
                        SHOP_GEO_KEY,
                        new Point(longitude, latitude),
                        shop.getId().toString()
                );
            }
        }
    }


    @Override
    public List<Shop> selectShopByType(Integer typeId, Long userId, double radiusKm) {
        List<Shop> shops = shopMapper.selectShopByType(typeId);
        // 从 Redis 里获取用户的经纬度
        Point userPoint = redisTemplate.opsForGeo()
                .position("用户经纬度", userId.toString())
                .get(0);

        if (userPoint == null) {
            return shops;
        }

        double userLon = userPoint.getX();
        double userLat = userPoint.getY();

        // 从 Redis 里查找附近店铺及距离
        GeoResults<RedisGeoCommands.GeoLocation<Object>> results =
                redisTemplate.opsForGeo().radius(
                        SHOP_GEO_KEY,
                        new Circle(userPoint, new Distance(radiusKm, Metrics.KILOMETERS)),
                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending()
                );

        // 让门店id和距离简历Map映射
        Map<String, Double> distanceMap = results.getContent().stream()
                .collect(Collectors.toMap(
                        r -> r.getContent().getName().toString(),
                        r -> r.getDistance().getValue()
                ));

        // 返回该服务分类+附近门店
        return shops.stream()
                .filter(shop -> distanceMap.containsKey(shop.getId().toString()))
                .peek(shop -> {
                    Double rawDistance = distanceMap.get(shop.getId().toString());
                    // 保留1位小数（四舍五入）
                    Double formattedDistance = new BigDecimal(rawDistance)
                            .setScale(1, RoundingMode.HALF_UP)
                            .doubleValue();
                    shop.setDistance(formattedDistance);

                })
                .collect(Collectors.toList());
    }

    @Override
    public List<Shop> searchShopsByName(String name) {
        if (name == null || name.trim().isEmpty()) {
            return shopMapper.selectShopList(new Shop()); // 返回全部
        }

        // 使用 match query 支持中文分词
        NativeSearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.boolQuery()
                        .should(QueryBuilders.matchQuery("name", name).analyzer("ik_smart"))   // 粗分提高精确度
                        .should(QueryBuilders.matchQuery("name", name).analyzer("ik_max_word")) // 细分增加召回率
                )
                .build();

        List<SearchHit<Shop>> hits = esTemplate.search(query, Shop.class).getSearchHits();


        return hits.stream()
                .map(SearchHit::getContent)
                .collect(Collectors.toList());

    }

    @Override
    public void syncShopsToEs() {
        List<Shop> shopList = shopMapper.findAll(); // 查询数据库所有商铺
        if (shopList != null && !shopList.isEmpty()) {

            List<Shop> esShopList = shopList.stream().map(shop -> {
                Shop esShop = new Shop();
                esShop.setId(shop.getId());
                esShop.setName(shop.getName());
                esShop.setOperatingArea(shop.getOperatingArea());

                // 同步数据库的真实时间
                esShop.setRegTime(null);
                esShop.setStartTime(shop.getStartTime());
                esShop.setEndTime(shop.getEndTime());

                esShop.setAddress(shop.getAddress());
                esShop.setLongitude(shop.getLongitude());
                esShop.setLatitude(shop.getLatitude());
                esShop.setDescription(shop.getDescription());
                esShop.setPhoto(shop.getPhoto());
                esShop.setRate(shop.getRate());
                esShop.setArea(shop.getArea());
                esShop.setStatus(shop.getStatus());
                esShop.setDeleted(shop.getDeleted());
                esShop.setAuditStatusId(shop.getAuditStatusId());

                return esShop;
            }).collect(Collectors.toList());

            // 批量写入 ES
            shopRepository.saveAll(esShopList);
        }
    }

    // 保存历史记录
    public void saveSearchHistory(Long userId, String keyword) {
        String key = HISTORY_KEY_PREFIX + userId;
        // 删除旧的重复项
        redisTemplate.opsForList().remove(key, 0, keyword);
        // 从左边推入
        redisTemplate.opsForList().leftPush(key, keyword);
        // 保持最多5条
        redisTemplate.opsForList().trim(key, 0, MAX_HISTORY - 1);
    }

    // 获取历史记录
    public List<Object> getSearchHistory(Long userId) {
        String key = HISTORY_KEY_PREFIX + userId;
        return redisTemplate.opsForList().range(key, 0, -1);
    }


    /**
     * 查询店铺：存储店铺的基本信息
     *
     * @param id 店铺：存储店铺的基本信息主键
     * @return 店铺：存储店铺的基本信息
     */
    @Override
    public Shop selectShopById(Integer id) {
        return shopMapper.selectShopById(id);
    }

    /**
     * 查询店铺：存储店铺的基本信息列表
     *
     * @param shop 店铺：存储店铺的基本信息
     * @return 店铺：存储店铺的基本信息
     */
    @Override
    public List<Shop> selectShopList(Shop shop) {
        return shopMapper.selectShopList(shop);
    }

    /**
     * 新增店铺：存储店铺的基本信息
     *
     * @param shop 店铺：存储店铺的基本信息
     * @return 结果
     */
    @Override
    public int insertShop(Shop shop) {
        return shopMapper.insertShop(shop);
    }

    /**
     * 修改店铺：存储店铺的基本信息
     *
     * @param shop 店铺：存储店铺的基本信息
     * @return 结果
     */
    @Override
    public int updateShop(Shop shop) {
        return shopMapper.updateShop(shop);
    }

    /**
     * 批量删除店铺：存储店铺的基本信息
     *
     * @param ids 需要删除的店铺：存储店铺的基本信息主键
     * @return 结果
     */
    @Override
    public int deleteShopByIds(Integer[] ids) {
        return shopMapper.deleteShopByIds(ids);
    }

    /**
     * 删除店铺：存储店铺的基本信息信息
     *
     * @param id 店铺：存储店铺的基本信息主键
     * @return 结果
     */
    @Override
    public int deleteShopById(Integer id) {
        return shopMapper.deleteShopById(id);
    }

    @Override
    public int addShop(Shop shop) {
        int i = shopMapper.insertShop(shop);
        if (i > 0)
            return shop.getId();
        else return -1;
    }

    @Override
    public Shop getShopInfo(String userName) {
        return shopMapper.selectShopInfo(userName);
    }
}
